/**
 *  记录常用的一些函数.
 *
 */
let myTools = (function () {

    /**========================================================
     * Types:
     * 包含的 apis:
     * isArray
     * isFunction
     * isObject
     * isArguments
     * isString
     * isNumber
     * isDate
     * isRegExp
     * isError
     * isSymbol
     * isMap
     * isWeakMap
     * isSet
     * isWeakSet
     *
     */
    const Types = {
        isArray(obj) {
            return Array.isArray(obj);
        },

        isFunction(obj) {
            return Object.prototype.toString.call(obj) === '[object Function]';
        },

        isObject(obj) {
            let type = typeof obj;
            return type === 'function' || type === 'object' && !!obj;
        },
    };
    ['Arguments', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'].forEach(name => {
        Types['is' + name] = function (obj) {
            return Object.prototype.toString.call(obj) === '[object ' + name + ']';
        }
    });

    // 深度clone
    function deepClone(obj) {
        let newObject;
        if (obj === null) {
            return null;
        } else if (!(obj instanceof Object)) {
            return obj;
        } else if (obj instanceof Date) {
            return new Date(obj);
        } else if (obj instanceof Function) {
            return Object.assign(obj);
        } else if (obj instanceof RegExp) {
            return new RegExp(obj);
        } else if (obj instanceof Array) {
            newObject = [];
            for (let item of obj) {
                newObject.push(deepClone(item));
            }
        } else {
            newObject = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
            for (let key of Object.keys(obj)) {

                if (newObject[key] !== obj) {
                    newObject[key] = deepClone(obj[key]);
                } else {
                    newObject[key] = newObject;
                }
            }
        }
        return newObject;
    }


    /**
     */
    function random(min, max) {
        if (max == null) {
            max = min;
            min = 0;
        }
        return min + Math.floor(Math.random() * (max - min + 1));
    }

    function clamp(x, minVal, maxVal) {
        return Math.min(Math.max(x, minVal), maxVal);
    }

    function smoothstep(edge0, edge1, x) {
        let t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
        return t * t * (3.0 - 2.0 * t);
    }

    // For element i of the return value, 0.0 is returned if x[i] < edge[i], and 1.0 is returned otherwise.
    function step(edge, x) {
        if (x < edge) {
            return 0;
        } else {
            return 1;
        }
    }

    // 会改变 arr
    function shuffle(arr) {
        let len = arr.length;
        for (let i = len - 1; i > 0; i--) {
            let j = Math.floor(Math.random() * (i + 1));
            [arr[i], arr[j]] = [arr[j], arr[i]];
        }
        return arr;
    }

    /**
     * [1,2,3,4,5,6,7] => [[1,2,3],[4,5,6],[7]]
     * @param arr
     * @param size
     */
    function sliceArrToMat2(arr, size) {
        let ret = [];
        let len = arr.length;
        if (len < 1) {
            return ret;
        }
        for (let i = 0; i < len; i += size) {
            ret.push(arr.slice(i, i + size));
        }
        return ret;
    }

    /**
     * 将 x 在 [a,b]区域, 映射到 在 [c,d] 的 x1
     * @param x
     * @param a
     * @param b
     * @param c
     * @param d
     * @return x1
     */
    function mapValue(x, a, b, c, d) {
        return (x - a) * (d - c) / (b - a) + c;
    }

    function mix(min, max, x) {
        return min * (1 - x) + max * x;
    }


    /**
     * 随机种子.
     * 可以生成确定性的随机数
     *  let func1 = seed(1234);
     *  let value = func1(); //  value 的值是确定性的.
     *
     * @param s
     * @return {function(): number}
     */
    function seed(s) {    // eslint-disable-line
        let m_w = s;
        let m_z = 987654321;
        let mask = 0xffffffff;

        return function () {
            m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
            m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;

            let result = ((m_z << 16) + m_w) & mask;
            result /= 4294967296;

            return result + 0.5;
        }
    }

    function asset(value, msg) {
        if (!value) {
            throw new Error(`error msg:${msg}`);
        }
    }

    let genId = function () {
        let _id = 0;
        return function () {
            return ++_id;
        }
    }();


    return {
        Types: Types,
        deepClone: deepClone,
        random: random,
        clamp: clamp,
        smoothstep: smoothstep,
        step: step,
        shuffle: shuffle,
        sliceArrToMat2: sliceArrToMat2,
        mapValue: mapValue,
        mix: mix,
        asset: asset,
        genId: genId,
    }

})();


// console.log(myTools.sliceArrToMat2([1,2,3,4,5,6,7],3));

window.myTools = myTools;
module.exports = myTools;
